/***************************************************************************************************
 *Copyright(C),2010-2017,Sumscope
 *FileName	:  DickChecker.h
 *Author	:  scofined.qi
 *Version	:  1.0
 *Date		:  2019/06/14
 *Desc		:  //用于主要说明此程序文件完成的主要功能
 *Relation :
 *Others	:  //其他内容说明
 *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
 *History	:
 * //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
 ***************************************************************************************************/
#pragma once

#include <qbmessage/include/message_bond.h>

#include "DictUpdateManager.h"

namespace qb {
namespace ht {
typedef qb::base::HighResTime HighResTime;
const char* GetDictTypeVersion(kDictType type);
kDictType GetDictTypeByVersion(const char* patchName);

class DickChecker : public IQpidMessageProcessor {
  /*
  检查等级
  首先,使用本地字典的序号数组,向服务器查询字典是否错误,得到一个UpdateID,
  检查等级为kCheckLevelByUpdateId
  其次,如果错误,将UpdateID设为0,检查等级升级为kCheckLevelBySequence
  再次,如果kCheckLevelBySequence检查依旧错误,则忽略,打严重日志,报告
  */
  enum kCheckLevel {
    kCheckLevelNoError,  // 正常
    kCheckLevelByUpdateId,  // 向服务器发送检查包,服务器告知应该更新对应版本
    kCheckLevelBySequence,  // 本地检查Sequence重复发现存在相同Sequence记录
    kCheckLevelServerError,  // 服务器字典数据存在故障
    kCheckLevelCount
  };
  struct Request {             // 每个请求一个此对象
    kDictType type;            // 当前请求的字典类型
    int seq_count;             // 当前请求的m_sequences的长度个数
    int index_start;           // 在vector<int> m_sequences中的索引开始
    int index_count;           // 在vector<int> m_sequences中的索引个数
    long req_id;               // PostRequestData的返回值
    bool ack_received;         // 响应是否已经收到了
    bool has_error;            // 是否出错
    int nzip_size, zlib_size;  // 两层压缩后的大小
    DWORD zip_time;            // 将序号数组压缩的时间
    HighResTime req_time;      // 发出请求的时间,
                               //	QBDictCheck_Req	req;			//
                               //发出去的请求,注意,m_Pack为无效
    QBDictCheck_Ack ack;       // 收到的响应
  };
  typedef DictUpdateManager::DictCheckInfo DictCheckInfo;
  typedef DictUpdateManager::FailedResult FailedResult;
  class ZipBuffer {
    char* m_buffer;
    int m_capcity;

   public:
    ZipBuffer() : m_buffer(0), m_capcity(0) {}
    ~ZipBuffer() { Clear(); }
    void Clear() {
      m_capcity = 0;
      if (m_buffer) delete[] m_buffer;
      m_buffer = NULL;
    }
    bool Reset(int capcity);
    operator char*() { return m_buffer; }
  };
  struct CheckInfo {
    kCheckLevel level;  //
    int updateid;       // level为kCheckLevelByUpdateId才有效
  };
  typedef std::map<kDictType, CheckInfo> CheckLevelMap;
  typedef std::map<long, Request> RequestMap;

 private:                        // 当前检查字典状态
  std::vector<int> m_sequences;  // 当前的sequences
  int m_seq_index;               // 当前的sequences的索引
  int m_seq_end;                 // 当前请求的sequences的索引的末尾
  ZipBuffer m_nzip_buffer;  // nzip使用的缓冲区,第一层压缩的缓冲区
  ZipBuffer m_zlib_buffer;  // zlib使用的缓冲区,第二层压缩的缓冲区,最终结果

  kDictType m_current_dict;  // 当前检查的dict
 private:                    // 当前检查的所有状态
  RequestMap m_requests;  // 当前的所有发出的请求,下次检测时清空
  FailedResult m_failed_results;  // 当前的检测错误结果,下次检测时清空
  FailedResult m_last_results;  // 上次的检测错误结果
  bool m_is_checking;           // 正在检查
 private:                       // 检查的时间信息
  HighResTime m_login_time;     // QB登录时间,也是字典线程启动时间
  HighResTime m_start_time;     // 起始时间
  HighResTime
      m_next_check;  // 下次自动检测时间,每次自动检测成功后,自动增加24小时
 protected:          // 不变成员
  bool m_check_zip;  // DictCheckZipValidate控制,用于对检测包进行核对
  bool m_registered;
  DictManager& m_dict_manager;  // 字典管理器
  int m_check_count;  // 总的检查递增次数，每个检查增加一次
  DWORD m_emergent_check;

 public:
  DickChecker(DictManager& dictManager);
  ~DickChecker();
  bool IsChecking() const { return m_is_checking; }
  void SetNextCheck(HighResTime time);
  void SetCheckLatter(const char* cause);
  bool StartCheck(bool autoCheck = true);
  void PrintDictState(bool start) const;
  bool CheckFinished(long reqid, const QBDictCheck_Ack& ack);
  void UpdateNextTime();
  void CancelCheck();
  bool HasCheckError() const { return m_failed_results.size() > 0; }
  bool HasServerError() const;
  void GetCheckResult(FailedResult& failedResults);
  bool IsDictFinished() const;
  kCheckLevel GetLevel(kDictType type, bool last) const;
  kCheckLevel GetCheckLevel(kDictType dict) const;
  kCheckLevel GetLastCheckLevel(kDictType dict) const;
  const char* GetLevelName(kCheckLevel level) const;
  void ClearCheckLevel(kDictType dict);
  void SetCheckLevel(kDictType dict, kCheckLevel level);

  STDMETHOD_(void, OnResponse)(THIS_ int32_t funcId, const qb::SSAckMsg&);

 protected:
  void AddDictCheckInfo(const QBDictCheck_Ack& ack, int nerror);
  bool CheckZip(char* buf, int length, int originalSize,
                const std::vector<int>& sequences, int start, int end);
  bool UpdateSequences();
  int ZipSequenceRange(int& zsize, int& nsize);
  bool SendCheckRequest();
  void CacheRequest(int nsize, int zsize, long id, DWORD tick);
  void CollectSeqFailure();
  int GetLastUpdateId(kDictType type) const;
};

}  // namespace ht
}  // namespace qb
